
#include <BLabel>
#include <BStyle>
#include <BHBoxLayout>
#include <BVBoxLayout>
#include <BGridLayout>
#include <BScrollBar>
#include <BMouseEvent>
#include <BFileDialog>

#include "PropertyBoard.h"

PropertyBoard::PropertyBoard(BWidget* parent) : BGroupBox(parent)
{
	this->setTitle("Property");

	BGridLayout* nameLayout = new BGridLayout();
	nameLayout->setColumnPolicy(0, Policy_Preferred | Policy_Expanding);
	{
		BLabel* nameLabel = new BLabel("name");
		_nameComboBox = new BComboBox();
		_nameComboBox->setEditable(true);

		BLabel* formatLabel = new BLabel("format");
		_formatEdit = new BLineEdit();

		nameLayout->addWidget(0, 0, nameLabel);
		nameLayout->addWidget(0, 1, _nameComboBox);
		nameLayout->addWidget(1, 0, formatLabel);
		nameLayout->addWidget(1, 1, _formatEdit);
	}

	_attribTable = new BTableWidget();
	_attribTable->setColumnCount(2);
	_attribTable->setColumnLabel(0, "attrib");
	_attribTable->setColumnLabel(1, "value");
	_attribTable->setGridStyle(Grid_Crossing);
	_attribTable->verticalScrollBar()->setFixedWidth(2);
	_attribTable->verticalScrollBar()->setWidthPolicy(Policy_Fixed);
	_attribTable->setAreaPolicy(Policy_Preferred | Policy_Expanding, Policy_Preferred);
	_attribTable->setColumnPolicy(Policy_Preferred | Policy_Expanding);

	_attribEdit = new BLineEdit(_attribTable);
	_attribEdit->setVisible(false);
	
	_attribsMenu = new BMenu(_attribTable);
	_attribsMenu->setName("attribs");
	_attribsMenu->addAction("remove");
	_attribsMenu->addAction("insert");

	_browseButton = new BButton("...", _attribTable);
	_browseButton->setVisible(false);

	_textEdit = new BTextEdit("Text");
	_textEdit->setReadOnly(true);
	_textEdit->setStyleSheet("handle-size:12px");
	{
		_plainCheckBox = new BCheckBox("plain");
		BHBoxLayout* topLayout = new BHBoxLayout(_textEdit, Part_Top);
		topLayout->addSpring();
		topLayout->addWidget(_plainCheckBox);
	}

	BVBoxLayout* vlayout = new BVBoxLayout(this);
	vlayout->setSpacing(3);
	vlayout->addLayout(nameLayout, 0);
	vlayout->addWidget(_attribTable);
	vlayout->addWidget(_textEdit);

	connect(this, Signal_AssetChanged, &PropertyBoard::slotAssetChanged);

	connect(_nameComboBox, Signal_EditFinished, &PropertyBoard::slotNameEditFinished);
	connect(_nameComboBox, Signal_CurrentChanged, &PropertyBoard::slotComboCurrentChanged);
	connect(_formatEdit, Signal_EditFinished, &PropertyBoard::slotFormatEditFinished);

	connect(_attribTable, Signal_ItemSelected, &PropertyBoard::slotAttribItemSelected);
	connect(_attribTable, Signal_RightClicked, &PropertyBoard::slotAttribItemRightClicked);
	connect(_attribTable, Signal_DoubleClicked, &PropertyBoard::slotAttribItemDoubleClicked);
	connect(_attribTable, Signal_Resized, &PropertyBoard::slotAttribTableResized);

	connect(_attribEdit, Signal_EditFinished, &PropertyBoard::slotAttribEditFinished);
	connect(_attribEdit, Signal_Focused, &PropertyBoard::slotAttribEditFocused);
	connect(_browseButton, Signal_Clicked, &PropertyBoard::slotBrowseButtonClicked);
	connect(_attribsMenu, Signal_Triggered, &PropertyBoard::slotAttribMenuTriggered);

	connect(_plainCheckBox, Signal_Checked, &PropertyBoard::slotPlainCheckBoxChecked);

	syncWidgets();

}
PropertyBoard::~PropertyBoard()
{

}

void PropertyBoard::syncWidgets()
{
	bool editable = _htmlWidget ? _htmlWidget->editable() : false;
	_nameComboBox->setEditable(editable);
	_formatEdit->setReadOnly(!editable);
	_textEdit->setReadOnly(!editable);
}
void PropertyBoard::freshRowLabels(int row)
{
	for (int r = row; r < _attribTable->rowCount(); r++)
	{
		_attribTable->setRowLabel(r, r);
	}
}

void PropertyBoard::slotAssetChanged(BObject* sender, const BValue& value)
{
	BHtmlWidget* htmlWidget = value;
	if (_htmlWidget != htmlWidget)
	{
		if (_htmlWidget)
		{
			_nameComboBox->clearItems();
			_formatEdit->setText(BString());
			_attribTable->clearItems();
			_textEdit->setText(BString());
			disconnect(_htmlWidget);
		}
		_htmlWidget = htmlWidget;
		if (_htmlWidget)
		{
			connect(_htmlWidget, Signal_Editable, &PropertyBoard::slotHtmlWidgetEditable);
			connect(_htmlWidget, Signal_ItemSelected, &PropertyBoard::slotHtmlItemSelected);
		}
	}
	syncWidgets();
}

void PropertyBoard::slotHtmlWidgetEditable(BObject* sender, const BValue& value)
{
	syncWidgets();
}
void PropertyBoard::slotHtmlItemSelected(BObject* sender, const BValue& value)
{
	BHtmlItem* item = value;
	if (!item->selected())
		item = 0;
	if (_htmlItem != item)
	{
		if (_htmlItem)
		{
			_nameComboBox->clearItems();
			_formatEdit->setText(BString());
			_attribTable->clearItems();
			_attribTable->setColumnPolicy(Policy_Growing);
			_textEdit->setText(BString());
			disconnect(_htmlItem);
		}
		_htmlItem = item;
		if (_htmlItem)
		{
			connect(_htmlItem, Signal_TextChanged, &PropertyBoard::slotHtmlItemTextChanged);
			const BStyle* style = this->realStyle();
			while (item)
			{
				BIcon* icon = style->icon(item->name());
				_nameComboBox->addItem(icon, item->name(), item);
				item = item->parent();
			}
			_nameComboBox->setCurrentIndex(0);
			BString format = _htmlItem->format();
			if (format.size())
			{
				format.replace("\n", "\\n");
				_formatEdit->setText(format);
			}
			const BMap<BString, BString>& attribMap = _htmlItem->attribMap();
			_attribTable->setRowCount(attribMap.size() + 1);
			for (auto it = attribMap.begin(); it.valid(); ++it)
			{
				const BString& attribName = it.key();
				const BString& attribValue = it.value();
				_attribTable->setItem(it.index(), 0, attribName);
				_attribTable->setItem(it.index(), 1, attribValue);
			}
			freshRowLabels(0);
			if (_plainCheckBox->checked())
				_textEdit->setText(_htmlItem->plainText());
			else
				_textEdit->setText(_htmlItem->text());
		}
	}
}
void PropertyBoard::slotHtmlItemTextChanged(BObject* sender, const BValue& value)
{
	if (_htmlItem != sender)
		return;
	_textEdit->setText(_htmlItem->text());
}

void PropertyBoard::slotNameEditFinished(BObject* sender, const BValue& value)
{
	const BString& text = _nameComboBox->text();
	_htmlItem->setName(text);
}
void PropertyBoard::slotComboCurrentChanged(BObject* sender, const BValue& value)
{
	if (BComboItem* comboItem = value)
	{
		BHtmlItem* item = comboItem->value();
		item->setSelected(true);
	}
}
void PropertyBoard::slotFormatEditFinished(BObject* sender, const BValue& value)
{
	BString format = value;
	format.replace("\\n", "\n");
	if (_htmlItem)
		_htmlItem->setFormat(format);
}

void PropertyBoard::slotAttribItemSelected(BObject* sender, const BValue& value)
{
	BTableItem* item = value;
	if (item->selected())
		_browseButton->setAsset(value);
	else
		_browseButton->setAsset(BValue());

	if (_browseButton->asset().empty())
	{
		_browseButton->hide();
		return;
	}
	BTableItem* nameItem = _attribTable->item(item->row(), 0);
	if (item->column() == 1 && nameItem && nameItem->text() == "src")
	{
		BRect rect = item->rect().align(Align_RightCenter, _browseButton->sizeHint());
		_browseButton->setRect(rect);
		_browseButton->show();
	}
	else
	{
		_browseButton->hide();
	}
}
void PropertyBoard::slotAttribItemRightClicked(BTableWidget* tableWidget, const BValue& value)
{
	const BMouseEvent* event = value.annex<BMouseEvent>();
	if (event->accepted())
		return;
	if (BTableItem* item = tableWidget->selectedItem())
	{
		if (item->column() == 0)
		{
			_attribsMenu->setPos(event->pos());
			_attribsMenu->popup();
			event->accept();
		}
	}
}
void PropertyBoard::slotAttribItemDoubleClicked(BTableWidget* tableWidget, const BValue& value)
{
	const BPoint& pos = value;
	BTableItem* item = _attribTable->itemAt(pos);
	if (!item)
	{
		int row = _attribTable->rowAt(pos.y());
		int column = _attribTable->columnAt(pos.x());
		if (row < 0 || column < 0)
			return;
		item = new BTableItem();
		_attribTable->setItem(row, column, item);
		item->setSelected(true);
	}
	BRect rect = _attribTable->cellRect(item->row(), item->column());
	_attribEdit->setAsset(item);
	_attribEdit->setRect(rect);
	_attribEdit->setText(item->text());
	_attribEdit->popup();
}
void PropertyBoard::slotAttribTableResized(BTableWidget* tableWidget, const BValue& value)
{
	BTableItem* item = _attribTable->asset();
	if (item && _attribEdit->visible())
	{
		BRect rect = tableWidget->cellRect(item->row(), item->column());
		_attribEdit->setRect(rect);
	}
}

void PropertyBoard::slotAttribEditFinished(BObject* sender, const BValue& value)
{
	_attribEdit->hide();
	BTableItem* item = _attribEdit->asset();
	const BString& text = _attribEdit->text();
	if (item->column() == 0)
	{
		if (text.empty())
		{
			_htmlItem->removeAttrib(item->text());
			_attribTable->removeRow(item->row());
			freshRowLabels(item->row());
			this->emit(Signal_Changed, _htmlWidget);
			return;
		}
		else if (_attribTable->item(item->row(), 1) == 0)
		{
			item->setText(text);
			_attribTable->setItem(item->row(), 1, "");
			this->emit(Signal_Changed, _htmlWidget);
			return;
		}
	}
	item->setText(text);
	int row = item->row();
	BTableItem* nameItem = _attribTable->item(row, 0);
	BTableItem* valueItem = _attribTable->item(row, 1);
	if (nameItem && valueItem)
	{
		_htmlItem->setAttrib(nameItem->text(), valueItem->text());
		this->emit(Signal_Changed, _htmlWidget);
	}
}
void PropertyBoard::slotAttribEditFocused(BObject* sender, const BValue& value)
{
	if (value == false)
	{
		_attribEdit->hide();
	}
}
void PropertyBoard::slotBrowseButtonClicked(BObject* sender, const BValue& value)
{
	if (BTableItem* valueItem = sender->asset())
	{
		BString fileName = BFileDialog::OpenFile("Open file", "", "*.png,*.jpg,.bmp,.gif");
		if (fileName.size())
		{
			fileName = BFolder::RelativePath(fileName);
			valueItem->setText(fileName);

			BTableItem* nameItem = _attribTable->item(valueItem->row(), 0);
			_htmlItem->setAttrib(nameItem->text(), valueItem->text());
			this->emit(Signal_Changed, _htmlWidget);
		}
	}
}
void PropertyBoard::slotAttribMenuTriggered(BObject* sender, const BValue& value)
{
	if (BAction* action = value)
	{
		if (action->text() == "insert")
		{
			this->emit(Signal_Changed, _htmlWidget);
		}
		if (action->text() == "remove")
		{
			if (BTableItem* item = _attribTable->selectedItem())
			{
				_attribTable->removeRow(item->row());
				freshRowLabels(item->row());
				this->emit(Signal_Changed, _htmlWidget);
			}
		}
	}
}

void PropertyBoard::slotPlainCheckBoxChecked(BObject* sender, const BValue& value)
{
	if (bool checked = value)
		_textEdit->setText(_htmlItem->plainText());
	else
		_textEdit->setText(_htmlItem->text());
}

void PropertyBoard::styleEvent(const BEvent& event)
{
	BGroupBox::styleEvent(event);
	const BStyle* style = this->realStyle();
	if (style)
	{
	}
}
